前回の内容
前回は、一覧ページの実装をしました↓
https://sakublog.tech/handson/express-app-2
今回は商品を追加できるようにします。
body-parserのインストール
ejsからフォームで送信した値をExpress側で受け取るには
body-parserというライブラリ
https://www.npmjs.com/package/body-parser
を使用します。
npm i body-parser
app.jsに追記します。
import express from "express";
import mysql from "mysql2/promise";
import bodyParser from "body-parser"; // 追記
import "dotenv/config";
const app = express();
const port = 3000;
app.set("view engine", "ejs");
app.use(express.static("public"));
app.use(bodyParser.urlencoded({ extended: false })); // 追記
これで、Expressでフォームからのデータを受け取れるようになります。
extendedは、データをエンコードするかのオプションで、今回は特に必要ないのでfalseにしています。
ExpressでPOST送信を受け取る
まずはadd.ejsのフォーム送信のactionを決めます。
今回は/addに送信します。
<form action="/add" method="POST">
こちらをapp.jsに追記します↓
app.post("/add", (req, res) => {
console.log(req.body);
res.redirect("/");
});
フォームの送信データは、req.body
で受け取ることができます。
その後、リダイレクトさせて一覧に遷移しています。
実際に画面から確認してみましょう。
ターミナルを確認すると、フォームで送信した値が受け取れています↓
バリデーション
不正な値ではないか確認しましょう。
今回は下記の通りにバリデーションを行います。
- 商品名
- 20文字以内
- 価格
- 1~999までの整数
- あったかいorつめたい
- 1か0
商品名のバリデーション
app.jsを次のように編集します。
app.post("/add", (req, res) => {
const errors = {
name: [],
price: [],
temp: [],
};
if (req.body.name.length > 20) {
errors.name.push("商品名は20文字以内です。");
}
if (errors.name.length) {
return res.render("add", { errors: errors });
}
res.redirect("/");
});
errorsにエラーの状態を管理します。
バリデーションエラーがあれば、errorsに追加します。
商品名はreq.body.name
で受け取れるので、20文字以内かをlengthで判定しています。
エラーがあった場合(error.name.lengthが1以上)、add.ejsにerrorsを渡してレンダリングします。
次に、add.ejsで、errorsを受けとってエラーの内容を表示します。
<label for="drink-name">商品名:</label>
<input type="text" id="drink-name" name="name" required />
<!-- 追加 -->
<p style="color: red"><%= errors.name.length ? errors.name[0] :"" %></p>
nameにエラーがあれば、表示させます。
これだと/addにアクセスしたときにエラーになるので、app.get(“/add”)
の方にもerrorsのオブジェクトを渡します。
app.get("/add", (req, res) => {
const errors = {
name: [],
price: [],
temp: [],
};
res.render("add", { errors: errors });
});
これで、商品名を20文字以上入れて追加すると、、、
このように、バリデーションで弾くことができました!
もっと使いやすくするには、バリデーション前のデータをフォームに入力された状態でエラーを表示するのが良いです。
今回は簡略化のため、なしとします。
価格のバリデーション
app.jsに追記していきます。
if (req.body.price >= 1000 || req.body.price <= 0) {
errors.price.push("価格は999円以内で設定できます。");
}
if (errors.name.length || errors.price.length) {
return res.render("add", { errors: errors });
}
res.render
のif文にも、priceを条件に追加します。
add.ejsの価格の箇所を変更します。商品名とほぼ同じ内容です。
<label for="drink-price">価格:</label>
<input type="number" id="drink-price" name="price" required />
<p style="color: red">
<%= errors.price.length ? errors.price[0] :"" %>
</p>
これで、値段を1000円以上か0以下で入力します。
すると、価格のところでエラーになりました!
これで価格のバリデーションができました。
温度のバリデーション
温度は、フロント側で不正な値に変えて確認するのはやや面倒です。
今回はコードだけ変更をします。
といっても、やることは商品名や価格と同じです。
app.jsに追記します。
tempの数字は文字列で渡ってくることに注意です。
if (req.body.temp !== "1" && req.body.temp !== "0") {
errors.temp.push("温度の選択肢が不正です。");
}
if (errors.name.length || errors.price.length || errors.temp.length) {
return res.render("add", { errors: errors });
}
add.ejs↓
<label for="drink-temp">温度:</label>
<select id="drink-temp" name="temp">
<option value="1">あたたかい</option>
<option value="0">冷たい</option>
</select>
<p style="color: red"><%= errors.temp.length ? errors.temp[0] :"" %></p>
これでOKです!
最後に、20や1000などのマジックナンバーは定数に移行しましょう。
app.jsに、3つの定数を追記します。
const port = 3000;
// 定数を追加
const MAX_NAME_LENGTH = 20;
const MAX_PRICE = 1000;
const MIN_PRICE = 0;
const TEMP_WARM = "1";
const TEMP_COLD = "0";
app.set("view engine", "ejs");
これらを呼び出すようにします↓
app.post("/add", (req, res) => {
const errors = {
name: [],
price: [],
temp: [],
};
if (req.body.name.length > MAX_NAME_LENGTH) {
errors.name.push("商品名は20文字以内です。");
}
if (req.body.price >= MAX_PRICE || req.body.price <= MIN_PRICE) {
errors.price.push("価格は999円以内で設定できます。");
}
if (req.body.temp !== TEMP_WARM && req.body.temp !== TEMP_COLD) {
errors.temp.push("温度の選択肢が不正です。");
}
if (errors.name.length || errors.price.length || errors.temp.length) {
return res.render("add", { errors: errors });
}
res.redirect("/");
});
これでバリデーションは完成です!
データベースに追加
バリデーションをパスした値を、データベースに追加します。
app.jsにINSERTする処理を追加します。
if (errors.name.length || errors.price.length || errors.temp.length) {
return res.render("add", { errors: errors });
}
// 追加
const connection = await mysql.createConnection({
host: process.env.MYSQL_HOST,
database: process.env.MYSQL_DATABASE,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
});
const sql = `INSERT INTO drinks (name,price,temperature) VALUES(?,?,?)`;
await connection.query(sql, [req.body.name, req.body.price, req.body.temp]);
await connection.end();
res.redirect("/");
queryメソッドで追加できます。
ユーザーからの値をSQLで使用するので、
プリペアドステートメントを使用することに注意してください。
(知らない方はプリペアドステートメントで検索すると出てきます)
ユーザーからの値を「?」にし、queryメソッドの第二引数の配列で順番に並べればOKです。
これで、追加できるか確認してみます。
追加されていれば完成です!
お疲れ様でした。
次回は更新機能を作成していきます!!!